﻿#pragma once

#include <memory>
#include "Render/Scene/PreRenderSceneNodes.h"
#include "Render/Scene/SceneParametersBuffer.h"


class CVulkanViewportWidget;
class CVulkanScene;
class CVulkanMesh;
class CVulkanMaterial;
class CVulkanUniformBuffer;

class CVulkanMeshInstance
{
public:
    CVulkanMeshInstance();
    // ~CVulkanMeshInstance();

    //绑定数据
    void SetData(const SPreRenderMeshInstance& InMeshInstance);

    // //在无光照阶段绘制mesh
    // void RecordUnlitDrawCommands(
    //     vk::CommandBuffer InCmdBuffer
    //     ,std::shared_ptr<CSceneParameterBuffer> SceneParameterBuffer
    // );

    // //在延迟渲染阶段绘制mesh
    // void RecordGBuffersDrawCommands(
    //     vk::CommandBuffer InCmdBuffer
    //     ,std::shared_ptr<CSceneParameterBuffer> SceneParameterBuffer
    // );

    //仅绘制顶点，不包含任何其他命令
    void RecordDrawVertexCommands(vk::CommandBuffer InCmdBuffer);

    vk::DescriptorSet GetModelUniformDescriptorSet() const
    {
        return ModelBufferDescriptorSet;
    }

    SMatrix4x4 GetModelMatrix() const;

    void CreateDescriptorSet();
protected:
    SVec3 Position;
    SQuat Rotation;
    SVec3 Scale;

    //包围球半径,用于快速剔除
    //这个半径需要缩放
    float BoundingSphereRadius;

    TVector<std::shared_ptr<CVulkanMesh>> MeshSections;
    TVector<std::shared_ptr<CVulkanMaterial>> Materials;
    bool CastShadow = true;
    

    //模型矩阵缓冲区，这个缓冲区就放一个矩阵
    std::shared_ptr<
        TVulkanUniformBuffer<CVulkanContext::SDefferShadingVertexModelUniform>
    > ModelUniformBuffer;

    //描述符集,仅包含模型矩阵缓冲区
    vk::DescriptorSet ModelBufferDescriptorSet=nullptr;

    friend class CVulkanMeshInstances;
};

//由相同网格和材质的网格实例组成的一组网格实例
//用于批量绘制以减少绘制指令
//由于着色器需要使用固定大小的批次
//所以这个批次还会被分成更小的批次
struct SVulkanMeshInstanceBatch
{
public:
    SVulkanMeshInstanceBatch();

    std::shared_ptr<CVulkanMesh> Mesh;
    std::shared_ptr<CVulkanMaterial> Material;
    TVector<SMatrix4x4> ModelMatrices;

    //在延迟渲染阶段绘制mesh
    void RecordGBuffersDrawCommands(
        vk::CommandBuffer InCmdBuffer
        ,std::shared_ptr<CSceneParameterBuffer> SceneParameterBuffer
    );

    //在无光照阶段绘制mesh
    void RecordUnlitDrawCommands(
        vk::CommandBuffer InCmdBuffer
        ,std::shared_ptr<CSceneParameterBuffer> SceneParameterBuffer
    );

    void CountDescriptorSet();

    //将模型矩阵数据更新到缓冲区
    void UpdateUniformBuffer();

    void CreateDescriptorSet();
protected:
    //模型矩阵缓冲区，这个缓冲区存放所有的模型矩阵
    std::shared_ptr<CVulkanUniformBuffer> ModelUniformBuffer;

    //每个批次会被分成固定大小的小批次
    //每个小批次对应一个描述符
    TVector<vk::DescriptorSet> ModelBufferDescriptorSets;
};

//由相同网格和相同光源的网格实例组成的一组网格实例
//用于在绘制光源的阴影贴图时批量绘制网格
//由于着色器需要使用固定大小的批次
//所以这个批次还会被分成更小的批次
struct SVulkanShadowmapMeshBatch
{
public:
    SVulkanShadowmapMeshBatch(std::shared_ptr<CVulkanMesh> InMesh);


    std::shared_ptr<CVulkanMesh> Mesh;
    TVector<SMatrix4x4> ModelMatrices;


    void CountDescriptorSet();

    void UpdateUniformBuffer();
    void CreateDescriptorSet();

    //模型矩阵缓冲区，这个缓冲区存放所有的模型矩阵
    std::shared_ptr<CVulkanUniformBuffer> ModelUniformBuffer;

    //每个批次会被分成固定大小的小批次
    //每个小批次对应一个描述符
    TVector<vk::DescriptorSet> ModelBufferDescriptorSets;

};

//管理网格实例
class CVulkanMeshInstances
{
public:
    // CVulkanMeshInstances();
    // ~CVulkanMeshInstances();

    void PreProcess(
        const TVector<SPreRenderMeshInstance>& InMeshInstance
        ,CVulkanViewportWidget* InViewport
        ,CVulkanScene* InScene
        );

    void CreateDescriptorSet();

    void RecordGBuffersDrawCommands(
        vk::CommandBuffer InCmdBuffer
        ,std::shared_ptr<CSceneParameterBuffer> SceneParameterBuffer
    );

    void RecordUnlitDrawCommands(
        vk::CommandBuffer InCmdBuffer
        ,std::shared_ptr<CSceneParameterBuffer> SceneParameterBuffer
    );

    //所有的网格实例,一一对应MeshInstanceData
    TVector<CVulkanMeshInstance> MeshInstances;

    struct SBatchKey
    {
        std::shared_ptr<CVulkanMesh> Mesh;
        std::shared_ptr<CVulkanMaterial> Material;

        uint64_t GetHashCode() const
        {
            return WH::HashCombine((uint64_t)Mesh.get(),(uint64_t)Material.get());
        }

        bool operator==(const SBatchKey& Other) const
        {
            return Mesh==Other.Mesh && Material==Other.Material;
        }
    };

    //所有在视口内的网格实例批次，用于批量绘制，所有的网格实例都在批次中
    TMap<SBatchKey, SVulkanMeshInstanceBatch> ViewportMeshInstanceBatches;

    //TVector<int64_t> ViewportMeshMap;

    //点光源网格映射,[光源索引][网格实例索引], 用于绘制阴影贴图
    TVector<TVector<int64_t>> PointLightShadowMapMeshMap;

    //聚光灯网格映射,[光源索引][网格实例索引], 用于绘制阴影贴图
    TVector<TVector<int64_t>> SpotLightShadowMapMeshMap;

    //方向光网格映射,[光源索引][网格][批次索引], 用于绘制阴影贴图
    TVector<
        TMap<
            std::shared_ptr<CVulkanMesh>
            ,SVulkanShadowmapMeshBatch
        >
    > DirectionalLightShadowMapMeshMap;
    
    
    

};
